package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
)

type CharCount struct {
	ChCount    int
	NumCount   int
	SpaceCount int
	OtherCount int
}

/** ·文件读写· */

// 1. read: 一次性读完文件并输出
func readFile() {
	file, err := os.Open("./test.txt") // 只读方式打开当前目录下的文件
	if err != nil {
		fmt.Println("open file failed!, err:", err)
		return
	}

	// 关闭文件
	defer file.Close()

	// 使用Read方法循环读取数据
	for {
		var tmp = make([]byte, 128)
		n, err := file.Read(tmp)
		if err == io.EOF {
			fmt.Println("文件读完了")
			return
		}
		if err != nil {
			fmt.Println("read file failed, err:", err)
			return
		}
		fmt.Println(string(tmp[:n]))
	}
}

// 2. bufio read: 带缓冲读取文件(默认4096)
func bufioReadFile() {
	file, err := os.Open("./test.txt")
	if err != nil {
		fmt.Println("bufio open file failed, err:", err)
		return
	}
	defer file.Close()

	reader := bufio.NewReader(file)
	for {
		line, err := reader.ReadString('\n') // 注意是字符
		if err == io.EOF {                   // end of file(如果读取到文件末尾)
			if len(line) != 0 {
				fmt.Println(line)
			}
			// 文件读完了则终止循环条件
			break
		}
		if err != nil {
			fmt.Println("read file failed, err:", err)
			return
		}
		fmt.Print(line)
	}
}

// 3. ioutil read: 一次性将文件读取到内存中(适合文件不大的情况)
func ioutilReadFile() {
	content, err := ioutil.ReadFile("./test.txt")
	if err != nil {
		fmt.Println("ioutil open file failed, err:", err)
		return
	}
	fmt.Println(string(content))
}

/**
os.OpenFile(name string, flag int, perm FileMode) (*File, error)
name: 文件地址 == filepath
flag: 文件打开模式(可以组合使用)
	const (
		os.O_RDONLY int = syscall.O_RDONLY   只读模式打开文件
		os.O_WRONLY int = syscall.O_WRONLY   只写模式打开文件
		os.O_RDWR int = syscall.O_RDWR	     读写模式打开文件
		os.O_APPEND	int = syscall.O_APPEND   追加写入
		os.O_CREATE int = syscall.O_CREATE   如果不存在则创建一个新文件
		os.EXCl	int = syscall.EXCl	         与O_CREATE结合使用，文件必须不存在
		os.SYNC	int = syscall.SYNC	         打开文件用于同步I/O
		os.TRUNC int = syscall.TRUNC	     如果可能，打开时清空文件内容
	)
perm: 指定的模式(文件权限)，如0666
ps: 如果操作成功，返回文件对象I/O，如果出错，错误底层类型*PathError
*/
// 4. write
func writeFile(filepath string) {
	file, err := os.OpenFile(filepath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	if err != nil {
		fmt.Println("write open file failed, err: ", err)
		return
	}
	defer file.Close()
	str := "hello world\n"
	file.Write([]byte(str))          // 写入字节切片数据
	file.WriteString("hello java\n") // 直接写入字符串数据
	fmt.Println("写入文件成功")
}

// 5. bufio write
func bufioWriteFile(filepath string) {
	file, err := os.OpenFile(filepath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
	if err != nil {
		fmt.Println("bufio open file failed, err: ", err)
		return
	}
	defer file.Close()
	bufioWriter := bufio.NewWriter(file)
	for i := 0; i < 10; i++ {
		bufioWriter.WriteString("hello 沙河小王子\n") // 将数据先写入缓存
	}
	bufioWriter.Flush() // 将缓存中的数据写入文件
}

// 6. ioutil write
func ioutilWriteFile(filepath string) {
	str := "hello 沙河娜扎\n"
	err := ioutil.WriteFile(filepath, []byte(str), 0666)
	if err != nil {
		fmt.Println("ioutil open file failed, err: ", err)
		return
	}
}

// 7. 练习: 将一个文件的内容写入到另外一个文件中(这两个文件已经存在): 使用outil.ReadFile、ioutil.WriteFile
func ioutilReadAndWriteFileContent(fromPath, toPath string) {
	isFromExist, notExistErr := pathExists(fromPath)
	if !isFromExist {
		fmt.Println("from filepath is not exist, err:", notExistErr)
		return
	}
	isToExist, notExistErr := pathExists(fromPath)
	if !isToExist {
		fmt.Println("to filepath is not exist, err:", notExistErr)
		return
	}
	content, r_err := ioutil.ReadFile(fromPath)
	if r_err != nil {
		fmt.Println("ioutil read file failed, err:", r_err)
		return
	}
	fmt.Println(string(content))
	w_err := ioutil.WriteFile(toPath, content, 0666)
	if w_err != nil {
		fmt.Println("ioutil open file failed, err: ", w_err)
		return
	}
	fmt.Println("文件读取以及写入完成!")
}

// 8. 练习: 统计文件内容中字母 数字 空格的个数
func charCountSum(filepath string) {
	var count CharCount
	file, err := os.Open(filepath)
	if err != nil {
		fmt.Println("file open err, err:", err)
	}
	defer file.Close()
	reader := bufio.NewReader(file)
	for {
		line, err := reader.ReadString('\n')
		if err == io.EOF {
			break
		}
		for _, v := range line {
			switch {
			case v >= 'a' && v <= 'z':
				fallthrough
			case v >= 'A' && v <= 'Z':
				count.ChCount++
			case v >= '0' && v <= '9':
				count.NumCount++
			case v == ' ' || v == '\t':
				count.SpaceCount++
			default:
				count.OtherCount++
			}
		}
	}
	fmt.Println("count:", count)

}

func main() {
	// 1. Open文件
	readFile()
	// 2. bufio: 封装api读取，支持string, line, lines等
	bufioReadFile()
	// 3. ioutil: 读取整个文件
	ioutilReadFile()
	// 4. write以及writeString
	writeFile("./test.txt")
	// 5. bufio.NewWriter
	bufioWriteFile("./test.txt")
	// 6. ioutil.WriteFile
	ioutilWriteFile("./test.txt")
	// 7. 练习
	ioutilReadAndWriteFileContent("./test.txt", "./abc.txt")
	// 8. 练习
	charCountSum("./count.txt") // count: {49 10 4 3}
}
